package cqrs

import java.util.concurrent.ConcurrentHashMap 
import java.util.concurrent.ConcurrentMap;

class CommandBus {
	
	private final ConcurrentMap handlers = [:] as ConcurrentHashMap
	private volatile InterceptorChain chain = new InterceptorChain()
	
	void register(Class commandType, Closure handler) {
		assert commandType
		assert handler
		handlers[commandType] = handler
		println "Command [$commandType] registered to handler [${handler.delegate}]"
	}
	
	def putAt(Class commandType, Closure handler) {
		register commandType, handler
	}
	
	void unregister(Class commandType, Closure handler) {
		handlers.remove commandType, handler
		println "Command [$commandType] unregister from handler [${handler.delegate}]"
	}
	
	void dispatch(command, Closure results) {
		// get a handler for command class
		Closure handler = handlers[command.getClass()]
		assert handler, "No Handler found for command [${command.getClass()}]"
		
		// stuff command, handler and results in context map for interceptors
		Map context = [command:command,handler:handler,results:results]
		
		// start interceptor chain, it finishes by invoking handler
		chain.proceed context
	}
	
	void intercept(Closure... interceptors) {
		this.chain = new InterceptorChain(interceptors)
	}
}

@PackageScope
class InterceptorChain {
	
	private final Closure current
	private final InterceptorChain next
	
	InterceptorChain(Closure... interceptors) {
		List list = interceptors as List
		if (list) {
			current = list[0]
			next = new InterceptorChain(list[1..<list.size] as Closure[])
		}
		else {
			current = null
			next = null
		}
	}
	
	void proceed(context) {
		if (next)
			current context, next
		else
			context.handler context.command, context.results
	}
}

